package cn.xclink.common.service;

import java.lang.reflect.Field;
import java.util.Date;
import java.util.List;

import javax.persistence.Id;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import cn.xclink.common.config.AppConfig;
import cn.xclink.common.mapper.BaseMapper;
import cn.xclink.kernel.entity.User;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

public abstract class BaseService<T> {

	@Autowired
	private HttpSession session;

	@Autowired
	private HttpServletRequest request;
	
	@Autowired
	private AppConfig config;

	public abstract BaseMapper<T> getMapper();

	/**
	 * 保存一个实体，null的属性也会保存，不会使用数据库默认值
	 * 
	 * @param entity
	 * @return
	 */
	public int save(T entity) throws Exception {
		try {
			User user = (User) request.getAttribute("currentUser");
			Class<?> claszz = entity.getClass();

			Field createBy = claszz.getDeclaredField("createBy");
			createBy.setAccessible(true);
			createBy.set(entity, user.getUserId());

			Field lastUpdateBy = claszz.getDeclaredField("lastUpdateBy");
			lastUpdateBy.setAccessible(true);
			lastUpdateBy.set(entity, user.getUserId());

			Field createTime = claszz.getDeclaredField("createTime");
			createTime.setAccessible(true);
			createTime.set(entity, new Date());

			Field lastUpdateTime = claszz.getDeclaredField("lastUpdateTime");
			lastUpdateTime.setAccessible(true);
			lastUpdateTime.set(entity, new Date());
		} catch (Exception e) {
			//e.printStackTrace();
		}
		return getMapper().insert(entity);
	}

	/**
	 * 保存一个实体，null的属性不会保存，会使用数据库默认值
	 * 
	 * @param entity
	 * @return
	 */
	public int saveNotNull(T entity) throws Exception {
		try {
			User user = (User) request.getAttribute("currentUser");
			Class<?> claszz = entity.getClass();

			Field createBy = claszz.getDeclaredField("createBy");
			createBy.setAccessible(true);
			createBy.set(entity, user.getUserId());

			Field lastUpdateBy = claszz.getDeclaredField("lastUpdateBy");
			lastUpdateBy.setAccessible(true);
			lastUpdateBy.set(entity, user.getUserId());

			Field createTime = claszz.getDeclaredField("createTime");
			createTime.setAccessible(true);
			createTime.set(entity, new Date());

			Field lastUpdateTime = claszz.getDeclaredField("lastUpdateTime");
			lastUpdateTime.setAccessible(true);
			lastUpdateTime.set(entity, new Date());
		} catch (Exception e) {
			//e.printStackTrace();
		}
		return getMapper().insertSelective(entity);
	}

	/**
	 * 批量插入，支持批量插入的数据库可以使用，例如MySQL,H2等，另外该接口限制实体包含`id`属性并且必须为自增列
	 * 
	 * @param recordList
	 * @return
	 */
	public int saveList(List<T> recordList) throws Exception {
		for(T entity : recordList){
			try {
				User user = (User) request.getAttribute("currentUser");
				Class<?> claszz = entity.getClass();

				Field lastUpdateBy = claszz.getDeclaredField("lastUpdateBy");
				lastUpdateBy.setAccessible(true);
				lastUpdateBy.set(entity, user.getUserId());

				Field lastUpdateTime = claszz.getDeclaredField("lastUpdateTime");
				lastUpdateTime.setAccessible(true);
				lastUpdateTime.set(entity, new Date());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return getMapper().insertList(recordList);
	}

	/**
	 * 根据主键更新实体全部字段，null值会被更新
	 * 
	 * @param record
	 * @return
	 */
	public int update(T entity) throws Exception {
		try {
			User user = (User) request.getAttribute("currentUser");
			Class<?> claszz = entity.getClass();

			Field lastUpdateBy = claszz.getDeclaredField("lastUpdateBy");
			lastUpdateBy.setAccessible(true);
			lastUpdateBy.set(entity, user.getUserId());

			Field lastUpdateTime = claszz.getDeclaredField("lastUpdateTime");
			lastUpdateTime.setAccessible(true);
			lastUpdateTime.set(entity, new Date());
		} catch (Exception e) {
			//e.printStackTrace();
		}		
		return getMapper().updateByPrimaryKey(entity);
	}
	
	/**
	 * 根据主键更新属性不为null的值
	 * 
	 * @param record
	 * @return
	 */
	public int updateNotNull(T entity) throws Exception {
		try {
			User user = (User) request.getAttribute("currentUser");
			Class<?> claszz = entity.getClass();

			Field lastUpdateBy = claszz.getDeclaredField("lastUpdateBy");
			lastUpdateBy.setAccessible(true);
			lastUpdateBy.set(entity, user.getUserId());

			Field lastUpdateTime = claszz.getDeclaredField("lastUpdateTime");
			lastUpdateTime.setAccessible(true);
			lastUpdateTime.set(entity, new Date());
		} catch (Exception e) {
			//e.printStackTrace();
		}
		return getMapper().updateByPrimaryKeySelective(entity);
	}

	/**
	 * 根据主键更新实体全部字段，null值会被更新
	 * 
	 * @param record
	 * @return
	 */
	public int saveOrUpdate(T entity) throws Exception {
		
		Object id = null;
		try {
			Class<?> claszz = entity.getClass();
			id = claszz.getDeclaredField(getPrimaryKeyName(entity)).get(entity);
		} catch (Exception e) {
			e.printStackTrace();
		}

		if (StringUtils.isEmpty(id)) {
			return save(entity);
		} else {
			return update(entity);
		}
	}
	
	/**
	 * 根据主键更新实体全部字段，null值会被更新
	 * 
	 * @param record
	 * @return
	 */
	public int saveOrUpdateNotNull(T entity) throws Exception {
		Object id = null;
		try {
			Class<?> claszz = entity.getClass();
			Field field = claszz.getDeclaredField(getPrimaryKeyName(entity));
			field.setAccessible(true);
			id = field.get(entity);
		} catch (Exception e) {
			e.printStackTrace();
		}

		if (StringUtils.isEmpty(id)) {
			return saveNotNull(entity);
		} else {
			return updateNotNull(entity);
		}
	}

	/**
	 * 根据主键字段进行删除，方法参数必须包含完整的主键属性
	 * 
	 * @param key
	 * @return
	 */
	public int delete(Object key) throws Exception {
		return getMapper().deleteByPrimaryKey(key);
	}

	/**
	 * 根据实体属性作为条件进行删除，查询条件使用等号
	 * 
	 * @param entity
	 * @return
	 */
	public int deleteByProperties(T record) throws Exception {
		return getMapper().delete(record);
	}
	
	
	/**
	 * 根据主键字段进行查询，方法参数必须包含完整的主键属性，查询条件使用等号
	 * 
	 * @param key
	 * @return
	 */
	public T get(Object key) {
		return getMapper().selectByPrimaryKey(key);
	}

	/**
	 * 查询全部结果
	 * 
	 * @return
	 */
	public List<T> findAll() {
		return getMapper().selectAll();
	}

	/**
	 * 根据实体中的属性进行查询，只能有一个返回值，有多个结果是抛出异常，查询条件使用等号
	 * 
	 * @param record
	 * @return
	 */
	public T findOne(T record) {
		return getMapper().selectOne(record);
	}

	/**
	 * 根据实体中的属性值进行查询
	 * 
	 * @param record
	 * @return
	 */
	public abstract List<T> find(T record);

	/**
	 * 根据实体中的属性值进行分页查询，返回PageInfo对象
	 * 
	 * @param record
	 * @param pageNum
	 * @param pageSize
	 * @return
	 */
	public PageInfo<T> findPageInfo(T record, Integer pageNum, Integer pageSize){
		
		if(pageNum == null){
			pageNum = Integer.parseInt(config.getValue("page.pageNum"));
		}
		if(pageSize == null){
			pageSize = Integer.parseInt(config.getValue("page.pageSize"));
		}
		PageHelper.startPage(pageNum, pageSize);
		List<T> list = find(record);
		return new PageInfo<T>(list);
	}
	
	/**
	 * 根据Example条件进行查询
	 * 
	 * @param example
	 * @return
	 */
	public List<T> findByExample(Object example) {
		return getMapper().selectByExample(example);
	}

	/**
	 * 根据example条件进行分页查询
	 * 
	 * @param example
	 * @param pageNum
	 * @param pageSize
	 * @return
	 */
	public List<T> findByExample(Object example, int pageNum, int pageSize) {
		return getMapper().selectByExampleAndRowBounds(example, new RowBounds(pageNum,pageSize));
	}

	/**
	 * 根据Example条件进行查询总数
	 * 
	 * @param example
	 * @return
	 */
	public int findCountByExample(Object example) {
		return getMapper().selectCountByExample(example);
	}

	/**
	 * 根据Example条件进行分页查询，查询条件使用等号，返回PageInfo对象
	 * 
	 * @param example
	 * @param pageNum
	 * @param pageSize
	 * @return
	 */
	public PageInfo<T> findPageInfoByExample(Object example, Integer pageNum, Integer pageSize) {
		
		if(pageNum == null){
			pageNum = Integer.parseInt(config.getValue("pageNum"));
		}
		if(pageSize == null){
			pageSize = Integer.parseInt(config.getValue("pageSize"));
		}
		
		PageHelper.startPage(pageNum, pageSize);
		List<T> list = findByExample(example);
		return new PageInfo<T>(list);
	}

	/**
	 * 获取主键对应的实体对象的属性名称
	 * 
	 * @return
	 */
	public String getPrimaryKeyName(T entity) {
		Field[] fields = entity.getClass().getDeclaredFields();
		String name = "";
		for (Field f : fields) {
			Id hidden = f.getAnnotation(Id.class);
			if (hidden != null) {
				name = f.getName();
				break;
			}
		}
		return name;
	}
}
